"""
cdn.admin_api

Defines the API for administering and provisioning a cdn server.
"""
### Start of imports

# This line just better work...
from jackson import debug

# There's debug stuff for the rest
debug.pre_import(__name__)

debug.importing(__name__, 'admin_api from jackson.chord as chord_admin')
from jackson.chord import admin_api as chord_admin
from jackson.chord import db

debug.importing(__name__, 'core, chord_api from jackson.cdn')
from jackson.cdn import core, chord_api

debug.importing(__name__, 'ContentEntry, StateEntry from jackson.cdn.models')
from jackson.cdn.models import ContentEntry, StateEntry

debug.importing(__name__, 'sync from jackson.cdn')
from jackson.cdn import sync

debug.importing(__name__, 'rttopt from jackson')
from jackson import rttopt

debug.post_import(__name__)

### End of imports

def init_net():
    """ Provisions server as the first server of a new CDN.
    @author: ep
    @rtype: True or False
    """
    context = [__name__, 'init_net()']
    debug.enter(context, None)
    try:
        debug.comment(context, 'Clearing cdn database')
        # Clear out database.
        core.clear_db()
        
        debug.comment(context, 'Initializing custodial range')
        # Initialize our state 'low' and state 'high'
        core.update_state('low', '0')
        core.update_state('high', '0')
        
        # Initialize a new CDN
        # Start up a new chord ring
        debug.comment(context, 'Initializing new chord ring')
        established_ring = chord_admin.init_ring()
        
        # If we were not successful, indicate failure
        if established_ring.has_key('exception'):
            debug.comment(context, 'Failed to establish new chord ring: ' + str(established_ring['exception']))
            debug.leave(context, established_ring)
            return established_ring
        
        # Once here, we are successful
        debug.comment(context, 'Successfully established new chord ring')
        
        # Initialize our state table
        debug.comment(context, 'Initializing state table')
        core.update_state('key', chord_api.get_me().key)
        
        debug.comment(context, 'updating my status to active')
        core.update_state('status', 'Active')
        
        debug.comment(context, 'Preparing response')
        established_ring['my_pred'] = db.get_predecessor() 
        established_ring[ 'my_succ'] = db.get_successor() 
        result = established_ring
    except Exception, e:
        debug.exception(context, str(e))
        result = { 'exception' : str(e) }
    
    # Ready to go
    debug.leave(context, result)
    return result

def join_net(server):
    """ Provisions this server as a new server joining the network that given server belongs to.
    @author: ep
    @rtype: True or False
    """
    context = [__name__, 'join_net()']
    debug.enter(context, { 'existing_server (chord.admin_api.join_net)' : server })
    try:
        # Clear out database.
        debug.comment(context, 'Clearing out cdn database')
        core.clear_db()
        
        # Initialize state 'low' and state 'high'
        debug.comment(context, 'Initializing custodial range')
        core.update_state('low', '0')
        core.update_state('high', '0')
        
        # Join an existing CDN
        # Start up a fresh chord server, joined to the specified ring
        debug.comment(context, 'Joining specified chord ring')
        # @bug: this line is raising an exception!!
        joined_ring = chord_admin.join_ring(server)
        # and report any failure
        if joined_ring.has_key('exception'):
            debug.comment(context, 'Failed to join chord ring: ' + joined_ring['exception'])
            debug.leave(context, joined_ring)
            return False
        
        # Once here we are successful
        debug.comment(context, 'Successfully joined chord ring')
        
        # Initialize our state table
        debug.comment(context, 'Initializing state table')
        core.update_state('key', chord_api.get_me().key)
        
        debug.comment(context, 'Activating')
        core.update_state('status', 'Active')
        
        debug.comment(context, 'Preparing response')
        result = joined_ring
    except Exception, e:
        debug.exception(context, str(e))
        result = { 'exception' : str(e) }
        
    # Ready to go
    debug.leave(context, result)
    return result

def leave_net():
    """ Induces server to disconnect from a CDN.
    @author: ep
    @rtype: True or False
    """
    context = [__name__, 'leave_net()']
    debug.enter(context, None)
    try:
        # Change my status to leaving
        debug.comment(context, "Updating status to 'Leaving'")
        core.update_state('status', 'Leaving')

        # Calling into rttopt leave logic
        rttopt.server_api.leave_net()
        
        # Disconnect from chord ring
        debug.comment(context, "Disconnecting from chord ring")
        left = chord_admin.leave_ring()
        # Report any failure
        if left.has_key('exception'):
            debug.comment(context, 'Failed to disconnect from chord ring')
            debug.leave(context, left)
            return left
        
        # Once here we are successful
        debug.comment(context, 'Successfully disconnected from chord ring')
        
        # Be sure to push off all files
        debug.comment(context, '(not) Waiting for all files to be transferred off')
        #debug.comment(context, 'NOTE: File support currently disabled; below lines should be uncommented to enable it')
        content_left = False
        max_failures = 128
        f = 0
        while content_left and f < max_failures:
            res = sync.poke_content()
            if res.has_key('exception'):
                debug.exception( res['exception'] )
            elif not res['success?']:
                content_left = False
            f += 1
        
        # We're all done here
        result = { 'success?' : True }
    except Exception, e:
        debug.exception(context, str(e))
        result = { 'exception' : str(e) }
    
    # Ready to go
    debug.leave(context, result)
    return result
